/**
 * @file cmath.h
 *
 * Proper inclusion of math functions and math constants
 * 
 * Purposes:
 * 	1. MSVC lacks a great number of std C math functions, need to workarond it.
 * 	2. Make it easier to switch to more efficient math libs in future.
 *
 * @author Dahua Lin
 */

#ifdef _MSC_VER
#pragma once
#endif

#ifndef SLIPP_MATH_BASE_H_
#define SLIPP_MATH_BASE_H_

#include <slipp/base/basic_defs.h>
#include <slipp/base/lang_utils.h>

#include <cmath>
#include <type_traits>


// useful math constants

#define SLI_M_E         2.71828182845904523536028747135266250   /* e */
#define SLI_M_LOG2E     1.44269504088896340735992468100189214   /* log 2e */
#define SLI_M_LOG10E    0.434294481903251827651128918916605082  /* log 10e */
#define SLI_M_LN2       0.693147180559945309417232121458176568  /* log e2 */
#define SLI_M_LN10      2.30258509299404568401799145468436421   /* log e10 */

#define SLI_M_PI        3.14159265358979323846264338327950288   /* pi */
#define SLI_M_PI_2X     6.28318530717958647692528676655900577   /* pi x 2 */
#define SLI_M_PI_2      1.57079632679489661923132169163975144   /* pi/2 */
#define SLI_M_PI_4      0.785398163397448309615660845819875721  /* pi/4 */
#define SLI_M_1_PI      0.318309886183790671537767526745028724  /* 1/pi */
#define SLI_M_2_PI      0.636619772367581343075535053490057448  /* 2/pi */
#define SLI_M_2_SQRTPI  1.12837916709551257389615890312154517   /* 2/sqrt(pi) */
#define SLI_M_SQRT2     1.41421356237309504880168872420969808   /* sqrt(2) */
#define SLI_M_SQRT1_2   0.707106781186547524400844362104849039  /* 1/sqrt(2) */

namespace sli
{
	namespace math
	{

		// inclusion C++03 standard ones

		using std::abs;
		using std::sqrt;
		using std::pow;

		using std::exp;
		using std::log;
		using std::log10;
		using std::frexp;
		using std::ldexp;

		using std::floor;
		using std::ceil;
		using std::fmod;

		using std::sin;
		using std::cos;
		using std::tan;
		using std::asin;
		using std::acos;
		using std::atan;
		using std::atan2;

		using std::sinh;
		using std::cosh;
		using std::tanh;

		// extras (for convenience)

		template<typename T>
		SLIPP_ENSURE_INLINE typename enable_if<is_arithmetic<T>::value, T>::type
		sqr(T x)
		{
			return x * x;
		}

		template<typename T>
		SLIPP_ENSURE_INLINE typename enable_if<is_arithmetic<T>::value, T>::type
		cube(T x)
		{
			return x * x * x;
		}

		template<typename T>
		SLIPP_ENSURE_INLINE typename enable_if<is_floating_point<T>::value, T>::type
		rcp(T x)
		{
			return T(1) / x;
		}

		template<typename T>
		SLIPP_ENSURE_INLINE typename enable_if<is_floating_point<T>::value, T>::type
		rsqrt(T x)
		{
			return T(1) / sqrt(x);
		}

		template<typename T>
		SLIPP_ENSURE_INLINE typename enable_if<is_arithmetic<T>::value, T>::type
		clamp(T x, T lb, T ub)
		{
			return x < lb ? lb : (x > ub ? ub : x);
		}

		// Part of the new stuff in C++0x (Unfortunately, many are not available for MSVC yet)

#if BCS_PLATFORM_INTERFACE==BCS_POSIX_INTERFACE

		using std::cbrt;
		using std::copysign;
		using std::hypot;

		using std::exp2;
		using std::log2;
		using std::logb;
		using std::expm1;
		using std::log1p;

		using std::round;
		using std::trunc;

		using std::asinh;
		using std::acosh;
		using std::atanh;

		using std::erf;
		using std::erfc;
		using std::lgamma;
		using std::tgamma;

		using std::isfinite;
		using std::isinf;
		using std::isnan;
		using std::signbit;

#else
		// TODO: implement C99 math for MSVC
#endif


	}
}

#endif
